package com.blackcat.dynamic.annotation.utils;

import org.apache.ibatis.javassist.ClassPool;
import org.apache.ibatis.javassist.CtClass;
import org.apache.ibatis.javassist.CtField;
import org.apache.ibatis.javassist.NotFoundException;
import org.apache.ibatis.javassist.bytecode.AnnotationsAttribute;
import org.apache.ibatis.javassist.bytecode.ConstPool;
import org.apache.ibatis.javassist.bytecode.FieldInfo;
import org.apache.ibatis.javassist.bytecode.annotation.Annotation;
import org.apache.ibatis.javassist.bytecode.annotation.MemberValue;
import org.apache.ibatis.javassist.bytecode.annotation.StringMemberValue;

/**
 * 动态添加注解
 * @author zhanghui
 * @since 2022/12/28 10:10
 */
public class DynamicAnnotationUtil {

    public static void main(String[] args) {
        String path = "com.blackcat.dynamic.annotation.entity.";
        System.out.println("-------加载初始状态-------");
        queryAnnotation(path+"TestEntity","name");
        queryAnnotation(path+"TestEntity","age");
        System.out.println();
        System.out.println("-------新增注解-------");
        addAnnotation(path+"TestEntity", "name","TableField");
        queryAnnotation(path+"TestEntity","name");
        System.out.println();
        System.out.println("-------新增注解属性-------");
        editAnnotationAttribute(path + "TestEntity", "name","TableField", "exist", "false");
        editAnnotationAttribute(path + "TestEntity", "name","TableField", "value", "111");
        queryAnnotation(path+"TestEntity","name");
        System.out.println();
        System.out.println("-------修改注解属性-------");
        System.out.println("修改前：");
        queryAnnotation(path+"TestEntity","age");
        editAnnotationAttribute(path + "TestEntity", "age","TableField", "exist", "true");
        System.out.println("修改后：");
        queryAnnotation(path+"TestEntity","age");
    }

    /**
     * 获取注解所有属性
     * @author : zhanghui
     * @since : 2022/12/28 14:25
     * @param className 全路径类名
     * @param attributeName 类属性名
    */
    public static void queryAnnotation(String className, String attributeName) {
        System.out.println("====开始遍历注解所有属性"+attributeName+"=====");
        try {
            ClassPool pool = ClassPool.getDefault();
            //获取实体类
            CtClass ct = pool.get(className);
            //获取属性
            CtField cf = ct.getField(attributeName);
            //获取属性字段信息
            FieldInfo fieldInfo = cf.getFieldInfo();
            //获取属性字段的运行时可见注释
            AnnotationsAttribute attribute = (AnnotationsAttribute) fieldInfo.getAttribute(AnnotationsAttribute.visibleTag);
            if (attribute != null) {
                //获取所有注解
                Annotation[] annotations = attribute.getAnnotations();
                int sum = 0;
                //遍历注解
                for (Annotation annotation : annotations) {
                    sum++;
                    System.out.println("注解" + sum + "：" + annotation.toString());
                    //如果没有属性名，就下一个循环
                    if (annotation.getMemberNames() == null) {
                        System.out.println("无属性名跟属性值！");
                        continue;
                    }
                    //获取注解的所有属性名，跟属性值
                    for (String memberName : annotation.getMemberNames()) {
                        MemberValue memberValue = annotation.getMemberValue(memberName);
                        System.out.println("获取到的注解的属性名：" + memberName);
                        System.out.println("获取到的注解的属性值：" + memberValue);
                    }
                }
            } else {
                System.out.println("该属性无注解");
            }
        } catch (NotFoundException e) {
            System.out.println("此类不存在" + e);
        }
    }

    /**
     * 给定的注解体，给其添加属性
     * @author : zhanghui
     * @since : 2022/12/28 14:23
     * @param className 全路径类名
     * @param attributeName 类属性名
     * @param annotationName 注解名
     * @param key 注解的属性
     * @param value 注解属性的值
    */
    public static void editAnnotationAttribute(String className, String attributeName, String annotationName, String key, String value) {
        try {
            ClassPool pool = ClassPool.getDefault();
            // 获取实体类
            CtClass ct = pool.get(className);
            // 获取属性
            CtField cf = ct.getField(attributeName);
            // 获取该字段的常量池
            ConstPool constPool = cf.getFieldInfo().getConstPool();
            // 获取属性字段信息
            FieldInfo fieldInfo = cf.getFieldInfo();
            // 获取属性字段的运行时可见注释
            AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(fieldInfo.getConstPool(), AnnotationsAttribute.visibleTag);
            // 获取注解
            Annotation annotation = getAnnotation(className, attributeName, annotationName);
            // 给指定的注解添加属性
            if (annotation != null) {
                annotation.addMemberValue(key, new StringMemberValue(value, constPool));
            }
            annotationsAttribute.addAnnotation(annotation);
            fieldInfo.addAttribute(annotationsAttribute);
            System.out.println("-----编辑属性：" + attributeName + " 注解：" + attributeName + " 属性：" + key + " 属性值：" + value);
        } catch (Exception e) {
            System.out.println("捕获异常" + e.getMessage());
        }
    }

    /**
     * 添加注解：用于本来没有注解，直接添加上一行整注解
     * @author : zhanghui
     * @since : 2022/12/28 14:17
     * @param className 全路径类名
     * @param attributeName 类属性名
     * @param annotationName 指定注解名
    */
    public static void addAnnotation(String className, String attributeName, String annotationName) {
        try {
            ClassPool pool = ClassPool.getDefault();
            // 获取实体类
            CtClass ct = pool.get(className);
            // 获取属性
            CtField cf = ct.getField(attributeName);
            // 获取属性字段信息
            FieldInfo fieldInfo = cf.getFieldInfo();
            // 获取属性字段的运行时可见注释
            AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(fieldInfo.getConstPool(), AnnotationsAttribute.visibleTag);
            // 添加注解
            Annotation tableField = new Annotation(annotationName, fieldInfo.getConstPool());
            annotationsAttribute.addAnnotation(tableField);
            fieldInfo.addAttribute(annotationsAttribute);
            System.out.println("属性："+attributeName+" 新增注解："+attributeName);
        } catch (NotFoundException e) {
            System.out.println("此类不存在" + e);
        }
    }

    /**
     * 获取指定字段的指定注解
     * @author : zhanghui
     * @since : 2022/12/28 14:01
     * @param className 全路径类名
     * @param attributeName 类属性名
     * @param annotationName 指定注解名
     */
    public static Annotation getAnnotation(String className, String attributeName, String annotationName) {
        try {
            ClassPool pool = ClassPool.getDefault();
            // 获取实体类
            CtClass ct = pool.get(className);
            // 获取属性
            CtField cf = ct.getField(attributeName);
            // 获取属性字段信息
            FieldInfo fieldInfo = cf.getFieldInfo();
            // 获取属性字段的运行时可见注释
            AnnotationsAttribute attribute = (AnnotationsAttribute) fieldInfo.getAttribute(AnnotationsAttribute.visibleTag);
            if (attribute != null) {
                // 获取该字段所有注解
                Annotation[] annotations = attribute.getAnnotations();
                for (Annotation annotation : annotations) {
                    // 注解名包含字段，就命中返回
                    if (annotation.toString().contains(annotationName)) {
                        return annotation;
                    }
                }
            }
        } catch (NotFoundException e) {
            System.out.println("捕获异常" + e.getMessage());
        }
        return null;
    }
}
